package com.wiscamp.ninechapters.tags.infrastructure.repositories;

import com.wiscamp.ninechapters.tags.domain.models.Course;
import com.wiscamp.ninechapters.tags.domain.models.CourseOutline;
import com.wiscamp.ninechapters.tags.domain.repositories.CourseRepository;
import com.wiscamp.ninechapters.tags.infrastructure.converters.CourseConverter;
import com.wiscamp.ninechapters.tags.infrastructure.mappers.CourseMapper;
import com.wiscamp.ninechapters.tags.infrastructure.mappers.CourseOutlineMapper;
import com.wiscamp.ninechapters.tags.infrastructure.mappers.CourseOutlineViewMapper;
import com.wiscamp.ninechapters.tags.infrastructure.mappers.CourseViewMapper;
import com.wiscamp.ninechapters.tags.infrastructure.po.CourseOutlinePO;
import com.wiscamp.ninechapters.tags.infrastructure.po.CoursePO;
import com.wiscamp.ninechapters.tags.infrastructure.po.CourseView;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.common.query.PagedObjects;
import cube.ddd.common.query.PagedQuery;
import cube.ddd.domain.BaseRepository;
import cube.ddd.utils.PersistenceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 课程知识点仓储对象
 */
@Repository
public class CourseRepositoryImpl extends BaseRepository implements CourseRepository {

    private final CourseMapper courseMapper;

    private final CourseViewMapper courseViewMapper;

    private final CourseOutlineMapper courseOutlineMapper;

    private final CourseOutlineViewMapper courseOutlineViewMapper;

    public CourseRepositoryImpl(CourseMapper courseMapper, CourseViewMapper courseViewMapper, CourseOutlineMapper courseOutlineMapper, CourseOutlineViewMapper courseOutlineViewMapper) {
        this.courseMapper = courseMapper;
        this.courseViewMapper = courseViewMapper;
        this.courseOutlineMapper = courseOutlineMapper;
        this.courseOutlineViewMapper = courseOutlineViewMapper;
    }


    //region course

    private CoursePO getCoursePO(@Min(0) long courseId) {
        return PersistenceUtils.findById(courseId, courseMapper::findById);
    }

    private void loadDetails(Course course) {
        if (Objects.nonNull(course)) {
            var outlines = getOutlinesByCourse(course.getCourseId());
            course.setOutlines(outlines);
        }
    }

    private void loadDetails(Collection<Course> courses) {
        if (courses.isEmpty()) return;
        var courseIds = courses.stream().map(Course::getCourseId).collect(Collectors.toList());
        var outlines = getOutlinesByCourseIds(courseIds);
        for (var course : courses) {
            var partialOutlines = outlines.stream().filter(o -> o.getCourseId() == course.getCourseId()).collect(Collectors.toList());
            course.setOutlines(partialOutlines);
        }
    }

    @HandleDataException
    public Course getCourse(@Min(0) long courseId) {
        var course = PersistenceUtils.findEntityById(courseId, courseViewMapper::findById,
                CourseConverter.Instance::toCourseByView);
        loadDetails(course);
        return course;
    }

    @HandleDataException
    public Course getCourseByCode(@NotBlank String courseCode) {
        var course = PersistenceUtils.getEntityByText(courseCode, courseViewMapper::findByCourseCode,
                CourseConverter.Instance::toCourseByView);
        loadDetails(course);
        return course;
    }

    @HandleDataException
    public Collection<Course> getCoursesByName(@NotBlank String courseName) {
        var courses = PersistenceUtils.getEntitiesByText(courseName, courseViewMapper::findListByCourseName,
                CourseConverter.Instance::toCourseByView);
        loadDetails(courses);
        return courses;
    }

    @HandleDataException
    public Collection<Course> getAvailableCourses() {
        var courses = PersistenceUtils.getEntities(courseViewMapper::findListByAvailable,
                CourseConverter.Instance::toCourseByView);
        loadDetails(courses);
        return courses;
    }

    @HandleDataException
    public Collection<Course> getCoursesByIds(@NotEmpty Collection<Long> courseIds) {
        var courses = PersistenceUtils.getEntitiesByIds(courseIds, courseViewMapper::findAllById,
                CourseConverter.Instance::toCourseByView);
        loadDetails(courses);
        return courses;
    }

    @HandleDataException
    public PagedObjects<Course> getCoursesByQuery(@NotNull PagedQuery query) {
        PagedObjects<CourseView> pagedObjects = PersistenceUtils.getPersistenceObjectsByQuery(query, courseViewMapper::findAll);
        return PersistenceUtils.convertPagedObjects(pagedObjects, CourseConverter.Instance::toCourseByView);
    }

    @HandleDataException
    public Course saveCourse(@NotNull Course course) {
        var newId = course.getCourseId();
        if (course.getCourseId() <= 0) {
            var newCourse = PersistenceUtils.createEntity(course, courseMapper::saveAndFlush,
                    CourseConverter.Instance::toCoursePO, CourseConverter.Instance::toCourse);
            newId = newCourse.getCourseId();
        } else {
            var existedPO = getCoursePO(course.getCourseId());
            if (existedPO == null) existedPO = new CoursePO();
            PersistenceUtils.updateEntity(course, existedPO, courseMapper::saveAndFlush,
                    CourseConverter.Instance::toCoursePO, CourseConverter.Instance::toCourse);
        }
        return getCourse(newId);
    }
    // endregion

    // region course outlines
    @HandleDataException
    private CourseOutlinePO getOutlinePO(long contentId) {
        return PersistenceUtils.findById(contentId, courseOutlineMapper::findById);
    }

    public CourseOutline getOutline(long outlineId) {
        return PersistenceUtils.findEntityById(outlineId, courseOutlineViewMapper::findById,
                CourseConverter.Instance::toCourseOutlineByView);
    }

    @HandleDataException
    public Collection<CourseOutline> getOutlinesByCourse(long courseId) {
        return PersistenceUtils.getEntitiesById(courseId, courseOutlineViewMapper::findListByCourse,
                CourseConverter.Instance::toCourseOutlineByView);
    }

    @HandleDataException
    public Collection<CourseOutline> getOutlinesByCourseIds(Collection<Long> courseIds) {
        return PersistenceUtils.getEntitiesByIds(courseIds, courseOutlineViewMapper::findListByCourseIds,
                CourseConverter.Instance::toCourseOutlineByView);
    }

    @HandleDataException
    public Collection<CourseOutline> getOutlinesByParent(long courseId, long parentId) {
        return PersistenceUtils.getEntitiesByIdAndId(courseId, parentId, courseOutlineViewMapper::findListByParent,
                CourseConverter.Instance::toCourseOutlineByView);
    }

    @HandleDataException
    public CourseOutline saveCourseOutline(@NotNull CourseOutline outline) {
        var newId = outline.getOutlineId();
        if (outline.getOutlineId() <= 0) {
            var newCourse = PersistenceUtils.createEntity(outline, courseOutlineMapper::saveAndFlush,
                    CourseConverter.Instance::toCourseOutlinePO, CourseConverter.Instance::toCourseOutline);
            newId = newCourse.getOutlineId();
        } else {
            var existedPO = getOutlinePO(outline.getOutlineId());
            if (existedPO == null) existedPO = new CourseOutlinePO();
            PersistenceUtils.updateEntity(outline, existedPO, courseOutlineMapper::saveAndFlush,
                    CourseConverter.Instance::toCourseOutlinePO, CourseConverter.Instance::toCourseOutline);
        }
        return getOutline(newId);
    }
    // endregion
}
